package com.ikas.ai.server.module.instance.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ikas.ai.server.module.alarm.dao.UnitDeteriorationAlarmDAO;
import com.ikas.ai.server.module.alarm.model.UnitDeteriorationAlarm;
import com.ikas.ai.server.module.data.dao.DataMeteDao;
import com.ikas.ai.server.module.data.model.DataMete;
import com.ikas.ai.server.module.instance.dorisDao.DorisWorkFlowInstanceDao;
import com.ikas.ai.server.module.instance.dorisDao.DorisWorkInstanceStepDao;
import com.ikas.ai.server.module.instance.dorisDao.DorisWorkInstanceStepDetailDao;
import com.ikas.ai.server.module.instance.enums.WorkInstanceStepDetailStateEnum;
import com.ikas.ai.server.module.instance.model.DorisWorkFlowInstance;
import com.ikas.ai.server.module.instance.model.DorisWorkInstanceStep;
import com.ikas.ai.server.module.instance.model.DorisWorkInstanceStepDetail;
import com.ikas.ai.server.module.instance.model.DorisWorkInstanceStepDetailCost;
import com.ikas.ai.server.module.instance.model.dto.WorkFlowBatchQuery;
import com.ikas.ai.server.module.instance.model.dto.WorkFlowInstanceDTO;
import com.ikas.ai.server.module.instance.model.dto.WorkInstanceStepDTO;
import com.ikas.ai.server.module.instance.model.dto.WorkInstanceStepDetailDTO;
import com.ikas.ai.utils.BeanUtils;
import com.ikas.ai.utils.CollectionUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author hu.bowei@ikasinfo.com
 * @date 2023-5-6 13:43:58
 */
@Service
public class DorisWorkFlowInstanceService extends ServiceImpl<DorisWorkFlowInstanceDao, DorisWorkFlowInstance> {

    @Autowired
    private DorisWorkInstanceStepDao dorisWorkInstanceStepDao;
    @Autowired
    private DorisWorkInstanceStepDetailDao dorisWorkInstanceStepDetailDao;
    @Autowired
    private UnitDeteriorationAlarmDAO unitDeteriorationAlarmDAO;
    @Autowired
    private DataMeteDao dataMeteDao;

    public List<DorisWorkFlowInstance> selectBatchList(WorkFlowBatchQuery query) {
        LambdaQueryWrapper<DorisWorkFlowInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DorisWorkFlowInstance::getMachineNo, query.getMachineNo());
        queryWrapper.eq(DorisWorkFlowInstance::getWfName, query.getWfName());
        queryWrapper.like(org.apache.commons.lang.StringUtils.isNotBlank(query.getBatch()), DorisWorkFlowInstance::getBatch, query.getBatch());
        queryWrapper.orderByDesc(DorisWorkFlowInstance::getId);
        queryWrapper.last("limit 20");
        return super.list(queryWrapper);
    }

    /**
     * 根据工况名称查询工况批次数据
     *
     * @param wfName
     * @return
     */
    public List<WorkFlowInstanceDTO> findWorkFlowInstance(String machineNo, String wfName) {
        LambdaQueryWrapper<DorisWorkFlowInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DorisWorkFlowInstance::getMachineNo, machineNo);
        queryWrapper.eq(DorisWorkFlowInstance::getWfName, wfName);
        // 工况列表
        List<DorisWorkFlowInstance> workFlowInstances = list(queryWrapper);

        List<WorkFlowInstanceDTO> workFlowInstanceDTOS = workFlowInstances.stream().map(item -> {
            WorkFlowInstanceDTO workFlowInstanceDTO = BeanUtils.copy(item, WorkFlowInstanceDTO.class);
            if (item.getCostTime() != null) {
                workFlowInstanceDTO.setCostTime(Double.valueOf(item.getCostTime()));
            }
            return workFlowInstanceDTO;
        }).collect(Collectors.toList());

        if (!CollectionUtil.isEmpty(workFlowInstanceDTOS)) {
            List<Long> flowInstanceIds = workFlowInstanceDTOS.stream().map(WorkFlowInstanceDTO::getId).collect(Collectors.toList());

            LambdaQueryWrapper<DorisWorkInstanceStep> workInstanceStepLambdaQueryWrapper = new LambdaQueryWrapper<>();
            workInstanceStepLambdaQueryWrapper.in(CollectionUtils.isNotEmpty(flowInstanceIds), DorisWorkInstanceStep::getWfInstanceId, flowInstanceIds);
            //加载步骤平均时长
//            Map<String, DorisWorkInstanceStepCost> stepCostMap = dorisWorkInstanceStepCostService.list(
//                    new LambdaQueryWrapper<DorisWorkInstanceStepCost>()
//                            .eq(DorisWorkInstanceStepCost::getMachineNo, machineNo)
//                            .eq(DorisWorkInstanceStepCost::getWfName, wfName)
//            ).stream().collect(Collectors.toMap(DorisWorkInstanceStepCost::getMeteCode, entity -> entity));

            // 工况步骤
            final BigDecimal divisor = new BigDecimal(1000);
            List<DorisWorkInstanceStep> workInstanceSteps = dorisWorkInstanceStepDao.selectList(workInstanceStepLambdaQueryWrapper);
            if (!CollectionUtil.isEmpty(workInstanceSteps)) {
                List<WorkInstanceStepDTO> workInstanceStepDTOS = workInstanceSteps.stream().map(item -> {
                    WorkInstanceStepDTO workInstanceStepDTO = BeanUtils.copy(item, WorkInstanceStepDTO.class);
                    workInstanceStepDTO.setDelay(Objects.isNull(workInstanceStepDTO.getDelay()) ? null : Double.valueOf(item.getDelay()));
                    workInstanceStepDTO.setAvgDelay(BigDecimal.valueOf(item.getAvgDelay()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                    return workInstanceStepDTO;
                }).collect(Collectors.toList());

                LambdaQueryWrapper<DorisWorkInstanceStepDetail> workInstanceStepDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
                workInstanceStepDetailLambdaQueryWrapper.in(CollectionUtils.isNotEmpty(flowInstanceIds), DorisWorkInstanceStepDetail::getWfInstanceId, flowInstanceIds);
                // 步骤详情
                List<DorisWorkInstanceStepDetail> workInstanceStepDetails = dorisWorkInstanceStepDetailDao.selectList(workInstanceStepDetailLambdaQueryWrapper);
                Map<Long, List<DorisWorkInstanceStepDetail>> step2InstanceMap = workInstanceStepDetails.stream().collect(Collectors.groupingBy(DorisWorkInstanceStepDetail::getStepInstanceId));
                // 工作转换实例响应平均耗时
                LambdaQueryWrapper<DorisWorkInstanceStepDetailCost> workInstanceStepCostLambdaQueryWrapper = new LambdaQueryWrapper<>();
                workInstanceStepCostLambdaQueryWrapper.eq(DorisWorkInstanceStepDetailCost::getMachineNo, machineNo);
                workInstanceStepCostLambdaQueryWrapper.eq(DorisWorkInstanceStepDetailCost::getWfName, wfName);
//                List<WorkInstanceStepDetailCost> workInstanceStepCosts = WorkInstance.selectList(workInstanceStepCostLambdaQueryWrapper);
//                Map<String, DorisWorkInstanceStepDetailCost> workInstanceStepCostMap = dorisWorkInstanceStepDetailCostService.list(
//                        new LambdaQueryWrapper<DorisWorkInstanceStepDetailCost>()
//                                .eq(DorisWorkInstanceStepDetailCost::getMachineNo, machineNo)
//                                .eq(DorisWorkInstanceStepDetailCost::getWfName, wfName)
//                ).stream().collect(Collectors.toMap(cost -> StringUtils.join(new Object[]{cost.getWfName(), cost.getStepName(), cost.getMeteCode()}, "_"), Function.identity()));
                workInstanceStepDTOS.forEach(workInstanceStepDTO -> {
                    List<DorisWorkInstanceStepDetail> list = step2InstanceMap.get(workInstanceStepDTO.getId());
                    if (!CollectionUtil.isEmpty(list)) {
                        List<WorkInstanceStepDetailDTO> detailDTOS = Lists.newArrayList();
                        list.forEach(workInstanceStepDetail -> {
                            WorkInstanceStepDetailDTO workInstanceStepDetailDTO = BeanUtils.convert(workInstanceStepDetail, WorkInstanceStepDetailDTO.class);
                            if (workInstanceStepDetailDTO.getLabelType() == 1 && workInstanceStepDetailDTO.getLeaf()) {
//                                String key = StringUtils.join(new Object[]{workInstanceStepDetailDTO.getWfName(), workInstanceStepDTO.getStepName(), workInstanceStepDetailDTO.getMeteCode()}, "_");
//                                DorisWorkInstanceStepDetailCost workInstanceStepCost = workInstanceStepCostMap.get(key);
                                workInstanceStepDetailDTO.setAvgCost(Objects.isNull(workInstanceStepDetail.getCostTime()) ? null : new BigDecimal(workInstanceStepDetail.getAvgCostTime()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                                Long costTime = workInstanceStepDetail.getCostTime();
                                if (costTime != null) {
                                    workInstanceStepDetailDTO.setCostTime(new BigDecimal(costTime).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                                }
                            }
                            if (!(workInstanceStepDetailDTO.getLabelType() == 2 && Objects.isNull(workInstanceStepDetailDTO.getLabel()))) {
                                //去除labelType=2并且meteCode为空的
                                detailDTOS.add(workInstanceStepDetailDTO);
                            }
                        });
                        workInstanceStepDTO.setDelay(Objects.isNull(workInstanceStepDTO.getDelay()) ? null : workInstanceStepDTO.getDelay() / 1000);
                        workInstanceStepDTO.setDetails(detailDTOS);
                    }
                });
                Map<Long, List<WorkInstanceStepDTO>> wfInstance2StepMap = workInstanceStepDTOS.stream().collect(Collectors.groupingBy(WorkInstanceStepDTO::getWfInstanceId));
                workFlowInstanceDTOS.forEach(workFlowInstanceDTO -> {
                    workFlowInstanceDTO.setSteps(wfInstance2StepMap.get(workFlowInstanceDTO.getId()));
                    if (workFlowInstanceDTO.getCostTime() != null) {
                        workFlowInstanceDTO.setCostTime(BigDecimal.valueOf(workFlowInstanceDTO.getCostTime()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                    }
                });
            }
        }
        return workFlowInstanceDTOS;
    }

    /**
     * 根据工况id查询工况详情数据
     *
     * @param id
     * @return
     */
    public WorkFlowInstanceDTO detail(Long id) {
        DorisWorkFlowInstance workFlowInstance = super.getById(id);
        if (null == workFlowInstance) {
            return null;
        }
        WorkFlowInstanceDTO workFlowInstanceDTO = BeanUtils.convert(workFlowInstance, WorkFlowInstanceDTO.class);
        LambdaQueryWrapper<DorisWorkInstanceStep> workInstanceStepLambdaQueryWrapper = new LambdaQueryWrapper<>();
        workInstanceStepLambdaQueryWrapper.eq(DorisWorkInstanceStep::getWfInstanceId, id);

        // 工况步骤
        BigDecimal divisor = new BigDecimal(1000);
        List<DorisWorkInstanceStep> workInstanceSteps = dorisWorkInstanceStepDao.selectList(workInstanceStepLambdaQueryWrapper);
        if (!CollectionUtil.isEmpty(workInstanceSteps)) {
            List<WorkInstanceStepDTO> workInstanceStepDTOS = workInstanceSteps.stream().map(item -> {
                WorkInstanceStepDTO workInstanceStepDTO = BeanUtils.copy(item, WorkInstanceStepDTO.class);
                workInstanceStepDTO.setDelay(Objects.isNull(workInstanceStepDTO.getDelay()) ? null : Double.valueOf(item.getDelay()));
                workInstanceStepDTO.setAvgDelay(new BigDecimal(item.getAvgDelay()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                return workInstanceStepDTO;
            }).collect(Collectors.toList());

            LambdaQueryWrapper<DorisWorkInstanceStepDetail> workInstanceStepDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
            workInstanceStepDetailLambdaQueryWrapper.eq(DorisWorkInstanceStepDetail::getWfInstanceId, id);
            // 步骤详情
            List<DorisWorkInstanceStepDetail> workInstanceStepDetails = dorisWorkInstanceStepDetailDao.selectList(workInstanceStepDetailLambdaQueryWrapper);
            Map<Long, List<DorisWorkInstanceStepDetail>> step2InstanceMap = workInstanceStepDetails.stream().collect(Collectors.groupingBy(DorisWorkInstanceStepDetail::getStepInstanceId));

            //根据instance查找告警
//            LambdaQueryChainWrapper<UnitDeteriorationAlarm> unitDeteriorationAlarmLambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(unitDeteriorationAlarmDAO);
//            List<UnitDeteriorationAlarm> deteriorationAlarmList = unitDeteriorationAlarmLambdaQueryChainWrapper.eq(UnitDeteriorationAlarm::getWcBatch, workFlowInstance.getBatch())
//                    .eq(UnitDeteriorationAlarm::getMachineNo, workFlowInstance.getMachineNo())
//                    .eq(UnitDeteriorationAlarm::getWcDesc, workFlowInstance.getWfName())
//                    .list();
//            Map<String, UnitDeteriorationAlarm> unitDeteriorationAlarmMap = deteriorationAlarmList.stream().collect(Collectors.toMap((i -> i.getStepName() + "_" + i.getMeteCode()), f -> f));
            LambdaQueryChainWrapper<DataMete> wrapper = new LambdaQueryChainWrapper<>(dataMeteDao);
            List<DataMete> meteList = wrapper.eq(DataMete::getMachineNo, workFlowInstance.getMachineNo()).list();
            meteList = meteList.stream().filter(distinctByVariable(DataMete::getMeteCode)).collect(Collectors.toList());
            Map<String, DataMete> dataMeteMap = meteList.stream().collect(Collectors.toMap(DataMete::getMeteCode, f -> f));
            workInstanceStepDTOS.forEach(workInstanceStepDTO -> {
                List<DorisWorkInstanceStepDetail> list = step2InstanceMap.get(workInstanceStepDTO.getId());
                if (!CollectionUtil.isEmpty(list)) {
                    List<WorkInstanceStepDetailDTO> detailDTOS = Lists.newArrayList();
                    list.forEach(workInstanceStepDetail -> {
                        WorkInstanceStepDetailDTO workInstanceStepDetailDTO = BeanUtils.convert(workInstanceStepDetail, WorkInstanceStepDetailDTO.class);
                        DataMete dataMete = dataMeteMap.get(workInstanceStepDetail.getMeteCode());
                        workInstanceStepDetailDTO.setMeteId(Objects.nonNull(dataMete) ? dataMete.getMeteId() : null);
                        if (workInstanceStepDetailDTO.getLabelType() == 1 && workInstanceStepDetailDTO.getLeaf()) {
                            workInstanceStepDetailDTO.setAvgCost(Objects.isNull(workInstanceStepDetail.getCostTime()) ? null : new BigDecimal(workInstanceStepDetail.getAvgCostTime()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                            Long costTime = workInstanceStepDetail.getCostTime();
                            if (costTime != null) {
                                workInstanceStepDetailDTO.setCostTime(new BigDecimal(costTime).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
                            }
                        }
//                        UnitDeteriorationAlarm unitDeteriorationAlarm = unitDeteriorationAlarmMap.get(workInstanceStepDetail.getStepName() + "_" + workInstanceStepDetail.getMeteCode());
//                        if (Objects.nonNull(unitDeteriorationAlarm)) {
//                            workInstanceStepDetailDTO.setState(WorkInstanceStepDetailStateEnum.ALARM.getCode());
//                        }
                        detailDTOS.add(workInstanceStepDetailDTO);
                    });
                    workInstanceStepDTO.setDelay(Objects.isNull(workInstanceStepDTO.getDelay()) ? null : workInstanceStepDTO.getDelay() / 1000);
                    workInstanceStepDTO.setDetails(detailDTOS);
                    DataMete mete = dataMeteMap.get(workInstanceStepDTO.getMeteCode());
                    workInstanceStepDTO.setMeteId(Objects.nonNull(mete) ? mete.getMeteId() : null);
                }
            });
            Map<Long, List<WorkInstanceStepDTO>> wfInstance2StepMap = workInstanceStepDTOS.stream().collect(Collectors.groupingBy(WorkInstanceStepDTO::getWfInstanceId));
            List<WorkInstanceStepDTO> workInstanceStepDTOS1 = wfInstance2StepMap.get(workFlowInstanceDTO.getId());
            workInstanceStepDTOS1 = workInstanceStepDTOS1.stream().sorted((o1, o2) -> (int) (o1.getStepId() - o2.getStepId())).collect(Collectors.toList());
            workFlowInstanceDTO.setSteps(workInstanceStepDTOS1);
            if (workFlowInstanceDTO.getCostTime() != null) {
                workFlowInstanceDTO.setCostTime(BigDecimal.valueOf(workFlowInstanceDTO.getCostTime()).divide(divisor, 3, RoundingMode.HALF_UP).doubleValue());
            }
        }
        return workFlowInstanceDTO;
    }

    private static <T> Predicate<T> distinctByVariable(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}
